package org.dromara.common.area.core.utils;

import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.text.csv.CsvUtil;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.area.core.Area;
import org.dromara.common.area.core.AreaHierarchy;
import org.dromara.common.area.core.enums.AreaTypeEnum;
import org.dromara.common.core.utils.ObjectUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static org.dromara.common.area.core.enums.AreaTypeEnum.*;
import static org.dromara.common.core.utils.CollectionUtils.convertList;
import static org.dromara.common.core.utils.CollectionUtils.findFirst;


/**
 * 区域工具类
 *
 * @author Ckj
 */
@Slf4j
public class AreaUtils {

    /**
     * 初始化 SEARCHER
     */
    @SuppressWarnings("InstantiationOfUtilityClass")
    private final static AreaUtils INSTANCE = new AreaUtils();

    /**
     * Area 内存缓存，提升访问速度
     */
    private static Map<Integer, Area> areas;

    private AreaUtils() {
        long now = System.currentTimeMillis();
        areas = new HashMap<>();
        areas.put(Area.ID_GLOBAL, new Area(Area.ID_GLOBAL, "全球", 0,
                null, new ArrayList<>()));
        // 从 csv 中加载数据
        List<CsvRow> rows = CsvUtil.getReader().read(ResourceUtil.getUtf8Reader("area.csv")).getRows();
        rows.remove(0); // 删除 header
        for (CsvRow row : rows) {
            // 创建 Area 对象
            Area area = new Area(Integer.valueOf(row.get(0)), row.get(1), Integer.valueOf(row.get(2)),
                    null, new ArrayList<>());
            // 添加到 areas 中
            areas.put(area.getId(), area);
        }

        // 构建父子关系：因为 Area 中没有 parentId 字段，所以需要重复读取
        for (CsvRow row : rows) {
            Area area = areas.get(Integer.valueOf(row.get(0))); // 自己
            Area parent = areas.get(Integer.valueOf(row.get(3))); // 父
            Assert.isTrue(area != parent, "{}:父子节点相同", area.getName());
            area.setParent(parent);
            parent.getChildren().add(area);
        }
        log.info("启动加载 AreaUtils 成功，耗时 ({}) 毫秒", System.currentTimeMillis() - now);
    }

    /**
     * 获得指定编号对应的区域
     *
     * @param id 区域编号
     * @return 区域
     */
    public static Area getArea(Integer id) {
        return areas.get(id);
    }

    /**
     * 获得指定区域对应的编号
     *
     * @param pathStr 区域路径，例如说：河南省/石家庄市/新华区
     * @return 区域
     */
    public static Area parseArea(String pathStr) {
        String[] paths = pathStr.split("/");
        Area area = null;
        for (String path : paths) {
            if (area == null) {
                area = findFirst(areas.values(), item -> item.getName().equals(path));
            } else {
                area = findFirst(area.getChildren(), item -> item.getName().equals(path));
            }
        }
        return area;
    }

    /**
     * 获取所有节点的全路径名称如：河南省/石家庄市/新华区
     *
     * @param areas 地区树
     * @return 所有节点的全路径名称
     */
    public static List<String> getAreaNodePathList(List<Area> areas) {
        List<String> paths = new ArrayList<>();
        areas.forEach(area -> getAreaNodePathList(area, "", paths));
        return paths;
    }

    /**
     * 构建一棵树的所有节点的全路径名称，并将其存储为 "祖先/父级/子级" 的形式
     *
     * @param node  父节点
     * @param path  全路径名称
     * @param paths 全路径名称列表，省份/城市/地区
     */
    private static void getAreaNodePathList(Area node, String path, List<String> paths) {
        if (node == null) {
            return;
        }
        // 构建当前节点的路径
        String currentPath = path.isEmpty() ? node.getName() : path + "/" + node.getName();
        paths.add(currentPath);
        // 递归遍历子节点
        for (Area child : node.getChildren()) {
            getAreaNodePathList(child, currentPath, paths);
        }
    }

    /**
     * 格式化区域
     *
     * @param id 区域编号
     * @return 格式化后的区域
     */
    public static String format(Integer id) {
        return format(id, " ");
    }

    /**
     * 格式化区域
     *
     * 例如说：
     * 1. id = “静安区”时：上海 上海市 静安区
     * 2. id = “上海市”时：上海 上海市
     * 3. id = “上海”时：上海
     * 4. id = “美国”时：美国
     * 当区域在中国时，默认不显示中国
     *
     * @param id        区域编号
     * @param separator 分隔符
     * @return 格式化后的区域
     */
    public static String format(Integer id, String separator) {
        // 获得区域
        Area area = areas.get(id);
        if (area == null) {
            return null;
        }

        // 格式化
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < AreaTypeEnum.values().length; i++) { // 避免死循环
            sb.insert(0, area.getName());
            // “递归”父节点
            area = area.getParent();
            if (area == null
                    || ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) { // 跳过父节点为中国的情况
                break;
            }
            sb.insert(0, separator);
        }
        return sb.toString();
    }

    /**
     * 获取指定类型的区域列表
     *
     * @param type 区域类型
     * @param func 转换函数
     * @param <T>  结果类型
     * @return 区域列表
     */
    public static <T> List<T> getByType(AreaTypeEnum type, Function<Area, T> func) {
        return convertList(areas.values(), func, area -> type.getType().equals(area.getType()));
    }

    /**
     * 根据区域编号、上级区域类型，获取上级区域编号
     *
     * @param id   区域编号
     * @param type 区域类型
     * @return 上级区域编号
     */
    public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) {
        for (int i = 0; i < Byte.MAX_VALUE; i++) {
            Area area = AreaUtils.getArea(id);
            if (area == null) {
                return null;
            }
            // 情况一：匹配到，返回它
            if (type.getType().equals(area.getType())) {
                return area.getId();
            }
            // 情况二：找到根节点，返回空
            if (area.getParent() == null || area.getParent().getId() == null) {
                return null;
            }
            // 其它：继续向上查找
            id = area.getParent().getId();
        }
        return null;
    }


    /**
     * 格式化区域ID字符串
     *
     * 例如说：
     * 1. idString = "110000,110100,110101"时：北京市 北京市 东城区
     * 2. idString = "110000,110100"时：北京市 北京市
     * 3. idString = "110000"时：北京市
     *
     * @param idString 逗号分隔的区域ID字符串，如"110000,110100,110101"
     * @param separator 分隔符，默认为空格
     * @return 格式化后的区域名称
     */
    public static String formatAreaIdString(String idString, String separator) {
        if (idString == null || idString.isEmpty()) {
            return "";
        }
        // 分割ID字符串
        String[] idArray = idString.split(",");
        // 存储找到的区域名称
        StringBuilder result = new StringBuilder();
        // 处理每个ID
        for (String idStr : idArray) {
            try {
                int id = Integer.parseInt(idStr.trim());
                Area area = areas.get(id);
                if (area != null) {
                    // 从当前区域开始向上查找父级区域
                    StringBuilder areaNames = new StringBuilder();
                    // 跳过中国和全球
                    if (ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) {
                        break;
                    }
                    // 添加区域名称
                    areaNames.insert(0, area.getName());
                    // 添加到结果中
                    if (result.length() > 0) {
                        result.append(separator);
                    }
                    result.append(areaNames);
                }
            } catch (NumberFormatException e) {
                // 忽略无效的ID
            }
        }
        return result.toString();
    }

    /**
     * 根据区域ID列表获取区域层级信息
     *
     * 例如输入 [110000, 110100, 110101]，返回北京市、北京市、东城区的层级信息
     *
     * @param areaIds 区域ID列表
     * @return 区域层级信息
     */
    public static AreaHierarchy getAreaHierarchy(List<Integer> areaIds) {
        AreaHierarchy hierarchy = new AreaHierarchy();

        if (areaIds == null || areaIds.isEmpty()) {
            return hierarchy;
        }
        // 从后向前遍历，确保区县、城市、省份的顺序
        for (int i = areaIds.size() - 1; i >= 0; i--) {
            Integer areaId = areaIds.get(i);
            Area area = getArea(areaId);
            if (area == null) {
                continue;
            }
            // 根据区域类型设置对应的层级信息
            switch (AreaTypeEnum.getByType(area.getType())) {
                case DISTRICT:
                    hierarchy.setDistrictId(areaId);
                    hierarchy.setDistrictName(area.getName());
                    break;
                case CITY:
                    hierarchy.setCityId(areaId);
                    hierarchy.setCityName(area.getName());
                    break;
                case PROVINCE:
                    hierarchy.setProvinceId(areaId);
                    hierarchy.setProvinceName(area.getName());
                    break;
                default:
                    // 忽略其他类型
                    break;
            }
        }
        return hierarchy;
    }

    /**
     * 根据区域ID获取区域层级信息
     *
     * 例如输入 110101(东城区)，返回包含北京市、北京市、东城区的层级信息
     *
     * @param areaId 区域ID
     * @return 区域层级信息
     */
    public static AreaHierarchy getAreaHierarchy(Integer areaId) {
        AreaHierarchy hierarchy = new AreaHierarchy();

        if (areaId == null) {
            return hierarchy;
        }

        Area area = getArea(areaId);
        if (area == null) {
            return hierarchy;
        }

        // 设置当前区域信息
        if (DISTRICT.getType().equals(area.getType())) {
            hierarchy.setDistrictId(areaId);
            hierarchy.setDistrictName(area.getName());
        } else if (CITY.getType().equals(area.getType())) {
            hierarchy.setCityId(areaId);
            hierarchy.setCityName(area.getName());
        } else if (PROVINCE.getType().equals(area.getType())) {
            hierarchy.setProvinceId(areaId);
            hierarchy.setProvinceName(area.getName());
        }

        // 向上查找父级区域
        Area parent = area.getParent();
        while (parent != null && !ObjectUtils.equalsAny(parent.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) {
            if (CITY.getType().equals(parent.getType())) {
                hierarchy.setCityId(parent.getId());
                hierarchy.setCityName(parent.getName());
            } else if (PROVINCE.getType().equals(parent.getType())) {
                hierarchy.setProvinceId(parent.getId());
                hierarchy.setProvinceName(parent.getName());
            }
            parent = parent.getParent();
        }

        return hierarchy;
    }

    /**
     * 根据城市ID获取该城市下的所有区
     *
     * @param cityId 城市ID
     * @return 区列表，如果城市不存在或没有区，返回空列表
     */
    public static List<Area> getDistrictsByCityId(Integer cityId) {
        List<Area> districts = new ArrayList<>();

        // 获取城市
        Area city = getArea(cityId);
        if (city == null) {
            log.warn("城市ID {} 不存在", cityId);
            return districts;
        }

        // 检查是否为城市类型
        if (!CITY.getType().equals(city.getType())) {
            log.warn("ID {} 不是城市类型，实际类型: {}", cityId, city.getType());
            districts.add(city);
            return districts;
        }

        // 获取子节点（区）
        List<Area> children = city.getChildren();
        if (children != null && !children.isEmpty()) {
            // 过滤出区类型
            for (Area child : children) {
                if (DISTRICT.getType().equals(child.getType())) {
                    districts.add(child);
                }
            }
        }

        return districts;
    }



}
